import {
  color
} from "../../../core/color";
import {
  any
} from "../../../core/logic";
import {
  Engine
} from "../../../Engine";
import {
  Pass
} from "../Pass";
import {
  Effect,
  EffectType
} from "./Effect";
import {
  fog_frag,
  fog_frag_multisampled
} from "./shaders/fog_frag";
import {
  fog_vert,
  fog_vert_multisampled
} from "./shaders/fog_vert";
import {
  foggbuffer_frag,
  foggbuffer_frag_multisampled
} from "./shaders/foggbuffer_frag";
/**
 * 后处理全局雾效
 * @class
 * @memberof w.effects
 */
class GlobalFog extends Effect {
  /**
   * @constructor
   * @param opts
   * @param opts.depthFogType {Number} 【可选，缺省:3.0】深度雾类型：0.0:不使用深度雾，1.0:距离深度雾，2.0:指数深度雾，3.0:指数平方深度雾。
   * @param opts.depthFogDensity {Number}【可选，缺省:0.001】深度雾强度,当depthFogType=2.0|3.0时有效。
   * @param opts.depthFogStart {Number}【可选，缺省:1】深度雾开始距离,当depthFogType=1.0时有效。
   * @param opts.depthFogEnd {Number}【可选，缺省:100000】深度雾结束距离,当depthFogType=1.0时有效。
   * @param opts.depthFogColor {String}【可选，缺省:"#ffffff"】深度雾颜色。
   * @param opts.depthFogAtten {Number}【可选，缺省:1.0】深度雾衰减。
   */
  constructor(opts = {}) {
    super(opts);
    this.type = EffectType.GlobalFog;
    this.sampler = Engine.instance.device.createSampler({
      magFilter: "linear", //nearest linear
      minFilter: "linear",
    });
    this.initBuffer();
    // this.initPipeLine();
    //深度雾部分
    this.depthFogType = any(opts.depthFogType, 3.0);
    this.depthFogDensity = any(opts.depthFogDensity, 0.001);
    this.depthFogStart = any(opts.depthFogStart, 1);
    this.depthFogEnd = any(opts.depthFogEnd, 100000);
    this.depthFogColor = any(opts.depthFogColor, "#ffffff");
    this.depthFogAtten = any(opts.depthFogAtten, 1.0);
    this.skyfogSmooth = any(opts.skyfogSmooth, 1.0);
    //高度雾部分
    this.heightFogType = any(opts.heightFogType, 0.0); //0:不启用  1：线性 2：指数 3：指数平方
    this.heightFogDensity = any(opts.heightFogDensity, 0.001);
    this.heightFogStart = any(opts.heightFogStart, 0);
    this.heightFogEnd = any(opts.heightFogEnd, 100);
    this.heightFogColor = any(opts.heightFogColor, "#ffffff");
    this.heightFogDistance = any(opts.heightFogDistance, 0.0);
    this.autoUpdate = true;
    this.update();
  }
  set depthFogType(v) {
    this._depthFogType = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 0, buffer);
    buffer = null;
    this.update();
  }
  get depthFogType() {
    return this._depthFogType;
  }
  set depthFogDensity(v) {
    this._depthFogDensity = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 4, buffer);
    buffer = null;
    this.update();
  }
  get depthFogDensity() {
    return this._depthFogDensity;
  }
  set depthFogStart(v) {
    this._depthFogStart = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 8, buffer);
    buffer = null;
    this.update();
  }
  get depthFogStart() {
    return this._depthFogStart;
  }
  set depthFogEnd(v) {
    this._depthFogEnd = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 12, buffer);
    buffer = null;
    this.update();
  }
  get depthFogEnd() {
    return this._depthFogEnd;
  }
  set depthFogColor(v) {
    this._depthFogColor = v;
    let buffer = color.format(v, false);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 16, buffer);
    buffer = null;
    this.update();
  }
  get depthFogColor() {
    return this._depthFogColor;
  }
  set depthFogAtten(v) {
    this._depthFogAtten = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 28, buffer);
    buffer = null;
    this.update();
  }
  get depthFogAtten() {
    return this._depthFogAtten;
  }
  get skyfogSmooth() {
    return this._skyfogSmooth;
  }
  set skyfogSmooth(v) {
    this._skyfogSmooth = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.depthFogBuffer, 32, buffer);
    buffer = null;
    this.update();
  }
  set heightFogType(v) {
    this._heightFogType = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 0, buffer);
    buffer = null;
    this.update();
  }
  get heightFogType() {
    return this._heightFogType;
  }
  set heightFogDensity(v) {
    this._heightFogDensity = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 4, buffer);
    buffer = null;
    this.update();
  }
  get heightFogDensity() {
    return this._heightFogDensity;
  }
  set heightFogStart(v) {
    this._heightFogStart = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 8, buffer);
    buffer = null;
    this.update();
  }
  get heightFogStart() {
    return this._heightFogStart;
  }
  set heightFogEnd(v) {
    this._heightFogEnd = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 12, buffer);
    buffer = null;
    this.update();
  }
  get heightFogEnd() {
    return this._heightFogEnd;
  }
  set heightFogColor(v) {
    this._heightFogColor = v;
    let buffer = color.format(v, false);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 16, buffer);
    buffer = null;
    this.update();
  }
  get heightFogColor() {
    return this._heightFogColor;
  }
  set heightFogDistance(v) {
    this._heightFogDistance = v;
    let buffer = new Float32Array([v]);
    Engine.instance.queue.writeBuffer(this.heightFogBuffer, 28, buffer);
    buffer = null;
    this.update();
  }
  get heightFogDistance() {
    return this._heightFogDistance;
  }
  initBuffer() {
    this.depthFogBuffer = Engine.instance.device.createBuffer({
      label: "depthFog",
      size: 48,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });
    this.heightFogBuffer = Engine.instance.device.createBuffer({
      label: "heightFog",
      size: 32,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });
  }
  initPipeLine() {
    if (!this.pipeLine) {
      this.pipeLine = Engine.instance.device.createRenderPipeline({
        label: "Fog Pipline",
        layout: `auto`,
        vertex: {
          module: Engine.instance.device.createShaderModule({
            code: fog_vert,
          }),
          entryPoint: "main",
        },
        fragment: {
          module: Engine.instance.device.createShaderModule({
            code: Engine.instance.enableGbuffer ? foggbuffer_frag : (Engine.instance.multisample ? fog_frag_multisampled : fog_frag),
          }),
          entryPoint: "main",
          targets: [{
            format: Engine.instance.format,
          },],
        },
        primitive: {
          topology: "triangle-list",
          cullMode: "back",
          frontFace: "ccw",
        },
      });
    }
  }
  initBindGroup(texture) {
    if (this.group && this.group.id != texture.id) {
      this.group = null;
    }
    if (!this.group) {
      const cameraBuffer = Engine.instance.scene.getCamera().getParam("camera")
        .gpu.buffer;
      this.group = Engine.instance.device.createBindGroup({
        label: "fog group",
        layout: this.pipeLine.getBindGroupLayout(0),
        entries: [{
          binding: 0,
          resource: this.sampler,
        },
        {
          binding: 1,
          resource: texture.view,
        },
        {
          binding: 2,
          resource: Engine.instance.enableGbuffer ?
            Pass.instance().postionTexture.view : Pass.instance().depthAttach.view,
        },
        {
          binding: 3,
          resource: {
            buffer: cameraBuffer,
            size: 432,
          },
        },
        {
          binding: 4,
          resource: {
            buffer: this.depthFogBuffer,
            size: 48,
          },
        },
        {
          binding: 5,
          resource: {
            buffer: this.heightFogBuffer,
            size: 32,
          },
        },
        ],
      });
      this.group.id = texture.id;
    }
    return this.group;
  }
  onRender(commandEncoder) {
    //执行帧渲染
    const pass = super.onRender(commandEncoder);
    this.initPipeLine()
    const texture = Pass.instance().readAttach;
    const renderPass = commandEncoder.beginRenderPass(pass);
    renderPass.setPipeline(this.pipeLine);
    let bindGroup = this.initBindGroup(texture);
    // set uniformGroup
    renderPass.setBindGroup(0, bindGroup);
    renderPass.draw(3);
    renderPass.end();
  }
  getDescript() {
    if (this._descript) {
      if (
        this._descript.size[0] !== Engine.instance.size[0] ||
        this._descript.size[1] !== Engine.instance.size[1]
      ) {
        this._descript = null;
      } else {
        this._descript.colorAttachments[0].view =
          Pass.instance().writeAttach.view;
      }
    }
    if (!this._descript) {
      this._descript = {
        colorAttachments: [{
          view: Pass.instance().writeAttach.view,
          loadOp: "clear",
          clearValue: {
            r: 1.0,
            g: 1.0,
            b: 1.0,
            a: 1.0
          },
          storeOp: "store",
        },],
      };
      this._descript.size = Engine.instance.size;
    }
    return this._descript;
  }
}
export {
  GlobalFog
};